package com.xuexiang.xui_lib.component.status;

import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentParent;
import ohos.agp.components.StackLayout;
import ohos.app.Context;

import java.util.HashMap;
import java.util.Map;

public class StatusLoader {

    /**
     * 加载中状态
     */
    public static final int STATUS_LOADING = 1;
    /**
     * 加载成功状态
     */
    public static final int STATUS_LOAD_SUCCESS = 2;
    /**
     * 加载失败状态
     */
    public static final int STATUS_LOAD_FAILED = 3;
    /**
     * 空数据状态
     */
    public static final int STATUS_EMPTY_DATA = 4;
    /**
     * 自定义状态
     */
    public static final int STATUS_CUSTOM = 5;

    private static volatile StatusLoader sDefault;

    private Adapter mAdapter;

    public interface Adapter {
        Component getView(Holder holder, Component convertView, int status);
    }

    public static StatusLoader getDefault() {
        if (sDefault == null) {
            synchronized (StatusLoader.class) {
                if (sDefault == null) {
                    sDefault = new StatusLoader();
                }
            }
        }
        return sDefault;
    }

    /**
     * init the default loading status view creator ({@link Adapter})
     *
     * @param adapter adapter to create all status views
     */
    public static void initDefault(Adapter adapter) {
        getDefault().mAdapter = adapter;
    }

    public static StatusLoader from(Adapter adapter) {
        StatusLoader statusLoader = new StatusLoader();
        statusLoader.mAdapter = adapter;
        return statusLoader;
    }

    /**
     * StatusLoader(loading status view) wrap the specific view.
     *
     * @param view view to be wrapped
     * @return Holder
     */
    public Holder wrap(Component view) {
        StackLayout wrapper = new StackLayout(view.getContext());
        ComponentContainer.LayoutConfig lp = view.getLayoutConfig();
        if (lp != null) {
            wrapper.setLayoutConfig(lp);
        }
        if (view.getComponentParent() != null) {
            ComponentContainer parent = (ComponentContainer) view.getComponentParent();
            int index = parent.getChildIndex(view);
            parent.removeComponent(view);
            parent.addComponent(wrapper, index);
        }
        ComponentContainer.LayoutConfig newLp = new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT);
        wrapper.addComponent(view, newLp);
        return new Holder(mAdapter, view.getContext(), wrapper);
    }

    /**
     * loadingStatusView shows cover the view with the same LayoutParams object
     * this method is useful with RelativeLayout and ConstraintLayout
     *
     * @param view the view which needs show loading status
     * @return Holder
     */
    public Holder cover(Component view) {
        ComponentParent parent = view.getComponentParent();
        if (parent == null) {
            throw new RuntimeException("view has no parent to show StatusLoader as cover!");
        }
        ComponentContainer viewGroup = (ComponentContainer) parent;
        StackLayout wrapper = new StackLayout(view.getContext());
        viewGroup.addComponent(wrapper, view.getLayoutConfig());
        return new Holder(mAdapter, view.getContext(), wrapper);
    }

    public static class Holder {
        private Adapter mAdapter;
        private Context mContext;
        private Component.ClickedListener mRetryListener;
        private ComponentContainer mWrapper;
        private Object mData;
        private int curState;
        private Component mCurStatusView;
        private Map<Integer, Component> mStatusViews = new HashMap<>(5);

        public Holder(Adapter mAdapter, Context mContext, ComponentContainer mWrapper) {
            this.mAdapter = mAdapter;
            this.mContext = mContext;
            this.mWrapper = mWrapper;
        }

        /**
         * 设置重试监听
         *
         * @param listener 重试监听
         * @return this
         */
        public Holder withRetry(Component.ClickedListener listener) {
            mRetryListener = listener;
            return this;
        }

        /**
         * set extension data
         *
         * @param data extension data
         * @return this
         */
        public Holder withData(Object data) {
            this.mData = data;
            return this;
        }

        public int getCurState() {
            return curState;
        }

        /**
         * show UI for status: {@link #STATUS_LOADING}
         */
        public void showLoading() {
            showLoadingStatus(STATUS_LOADING);
        }

        /**
         * show UI for status: {@link #STATUS_LOAD_SUCCESS}
         */
        public void showLoadSuccess() {
            showLoadingStatus(STATUS_LOAD_SUCCESS);
        }

        /**
         * show UI for status: {@link #STATUS_LOAD_FAILED}
         */
        public void showLoadFailed() {
            showLoadingStatus(STATUS_LOAD_FAILED);
        }

        /**
         * show UI for status: {@link #STATUS_EMPTY_DATA}
         */
        public void showEmpty() {
            showLoadingStatus(STATUS_EMPTY_DATA);
        }

        /**
         * show UI for status: {@link #STATUS_CUSTOM}
         */
        public void showCustom() {
            showLoadingStatus(STATUS_CUSTOM);
        }

        /**
         * Show specific status UI
         *
         * @param status status
         * @see #showLoading()
         * @see #showLoadFailed()
         * @see #showLoadSuccess()
         * @see #showEmpty()
         */
        public void showLoadingStatus(int status) {
            if (curState == status || !validate()) {
                return;
            }
            curState = status;
            //first try to reuse status view
            Component convertView = mStatusViews.get(status);
            if (convertView == null) {
                //secondly try to reuse current status view
                convertView = mCurStatusView;
            }
            try {
                //call customer adapter to get UI for specific status. convertView can be reused
                Component view = mAdapter.getView(this, convertView, status);
                if (view == null) {
                    System.out.println(mAdapter.getClass().getName() + ".getView returns null");
                    return;
                }
                if (view != mCurStatusView || mWrapper.getChildIndex(view) < 0) {
                    if (mCurStatusView != null) {
                        mWrapper.removeComponent(mCurStatusView);
                    }
//                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//                        view.setElevation(Float.MAX_VALUE);
//                    }
                    mWrapper.addComponent(view);
                    ComponentContainer.LayoutConfig lp = view.getLayoutConfig();
                    if (lp != null) {
                        lp.width = ComponentContainer.LayoutConfig.MATCH_PARENT;
                        lp.height = ComponentContainer.LayoutConfig.MATCH_PARENT;
                    }
                } else if (mWrapper.getChildIndex(view) != mWrapper.getChildCount() - 1) {
                    // make sure loading status view at the front
                    //view.bringToFront();
                    System.out.println("StatusLoader view.bringToFront()");
                }
                mCurStatusView = view;
                mStatusViews.put(status, view);
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("StatusLoader " + e.getMessage());
            }
        }

        private boolean validate() {
            if (mAdapter == null) {
                System.out.println("StatusLoader.Adapter is not specified！");
            }
            if (mContext == null) {
                System.out.println("StatusLoader Context is null！");
            }
            if (mWrapper == null) {
                System.out.println("StatusLoader The mWrapper of loading status view is null！");
            }
            return mAdapter != null && mContext != null && mWrapper != null;
        }

        public Context getContext() {
            return mContext;
        }

        /**
         * get wrapper
         *
         * @return container of StatusLoader
         */
        public ComponentContainer getWrapper() {
            return mWrapper;
        }

        /**
         * get retry task
         *
         * @return retry task
         */
        public Component.ClickedListener getRetryListener() {
            return mRetryListener;
        }

        /**
         * get extension data
         *
         * @param <T> return type
         * @return data
         */
        public <T> T getData() {
            try {
                return (T) mData;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    private StatusLoader() {

    }
}
